﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Specialized;
using FlexiCache;

namespace CLOComponentlibCSFixture.FlexiCache
{
    [TestClass]
    public class SqlDBCacheStorageAdapterTests
    {
        SqlDBCacheStorageAdapter _adapter;

        [TestInitialize]
        public void BeforeTest()
        {
            _adapter = new SqlDBCacheStorageAdapter();
            //override connection string to SQL for testing
            _adapter.IsEnabled = true;
            _adapter.CacheDatabaseName = "";
            _adapter.IsAcknowledgedWrite = true;
            _adapter.MaxDataSizeLength = 0;
            _adapter.TracePrefix = "SqlDBCacheStorageAdapter";
            _adapter.UseCompression = false;
            _adapter.DBConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["cacheConnection"].ConnectionString;
        }

        [TestMethod]
        public void Test001_DoConnectionTest()
        {
            _adapter.ConnectionTest();
        }

        [TestMethod]
        public void Test002_ShouldSaveNewObjectToCache()
        {
            string key = "12434";

            _adapter.Add(key, "Hello SqlDB", DateTime.UtcNow.AddSeconds(100), "");
            var obj = _adapter.Get(key);

            Assert.AreEqual("Hello SqlDB", obj);

            _adapter.Remove(key);
        }

        [TestMethod]
        public void Test003_ShouldGetExistingObjectFromCache()
        {
            string key = "98762";

            _adapter.Add(key, "Saved Item", DateTime.UtcNow.AddSeconds(100), "");
            var obj = _adapter.Get(key);

            Assert.AreEqual("Saved Item", obj);

            _adapter.Remove(key);
        }

        [TestMethod]
        public void Test004_ShouldNotGetNonExistentObjectFromCache()
        {
            string key = "90877000";

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test005_ShouldNotGetExpiredObjectFromCache()
        {
            string key = "98734234";

            _adapter.Add(key, "This has expired already!", DateTime.UtcNow.AddSeconds(-10), "");
            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test006_AddShouldNotUpdateExistingItemInCache()
        {
            string key = "9873";

            _adapter.RemoveAll();
            _adapter.Add(key, "Existing Item", DateTime.UtcNow.AddSeconds(100), "");
            _adapter.Add(key, "Updated Item", DateTime.UtcNow.AddSeconds(100), "");
            var obj = _adapter.Get(key);

            Assert.AreEqual("Existing Item", obj);
        }

        [TestMethod]
        public void Test007_AddShouldUpdateExpiredItemInCache()
        {
            string key = "564536787";

            _adapter.RemoveAll();
            _adapter.Add(key, "This has expired already!", DateTime.UtcNow.AddSeconds(-10), "");
            _adapter.Add(key, "This hasn't", DateTime.UtcNow.AddSeconds(10), "");
            var obj = _adapter.Get(key);

            Assert.AreEqual("This hasn't", obj);
        }

        [TestMethod]
        public void Test008_SetShouldUpdateExistingObjectFromCache()
        {
            string key = "45645464";

            _adapter.RemoveAll();
            _adapter.Add(key, "New Document", DateTime.UtcNow.AddSeconds(100), "");
            _adapter.TrySet(key, "Updated Document", DateTime.UtcNow.AddSeconds(100), "");
            var obj = _adapter.Get(key);

            Assert.AreEqual("Updated Document", obj);

            _adapter.Remove(key);
        }

        [TestMethod]
        public void Test009_ShouldRemoveValidObjectFromCache()
        {
            string key = "08977";

            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "");
            _adapter.Remove(key);

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test010_CountShouldBeValid()
        {
            _adapter.RemoveAll();
            _adapter.Add("089776466", "Document # 1", DateTime.UtcNow.AddSeconds(100), "");
            _adapter.Add("089776467", "Document # 2", DateTime.UtcNow.AddSeconds(100), "");

            long obj = _adapter.GetCount();

            Assert.AreEqual(2, obj, "The number of items should be 2.");
        }

        [TestMethod]
        public void Test011_ShouldBeAbleToRemoveAllItems()
        {
            _adapter.Add("089776468", "Document # 3", DateTime.UtcNow.AddSeconds(100), "");
            _adapter.Add("089776469", "Document # 4", DateTime.UtcNow.AddSeconds(100), "");

            _adapter.RemoveAll();
            long obj = _adapter.GetCount();

            Assert.AreEqual(0, obj, "The number of items should be 0.");
        }

        [TestMethod]
        public void Test012_ShouldRemoveExpiredItems()
        {
            _adapter.RemoveAll();
            _adapter.Add("089776446", "Document # 5", DateTime.UtcNow.AddSeconds(-100), "");
            _adapter.Add("089776447", "Document # 6", DateTime.UtcNow.AddSeconds(-100), "");
            _adapter.Add("089776448", "Document # 7", DateTime.UtcNow.AddSeconds(100), "");

            _adapter.RemoveExpired();
            long obj = _adapter.GetCount();

            Assert.AreEqual(1, obj, "The number of items should be 1.");
        }

        [TestMethod]
        [Ignore()]
        public void Test014_Speedtest1()
        {
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();
            System.Diagnostics.Stopwatch timer2 = new System.Diagnostics.Stopwatch();
            string key = "";
            byte[] data = null;
            Random rnd = new Random(DateTime.Today.Millisecond);
            int iterationCount = 100;
            System.Security.Cryptography.RandomNumberGenerator gen = System.Security.Cryptography.RandomNumberGenerator.Create();
            System.Text.StringBuilder sbr = new StringBuilder("Length,Duration\n");
            for (int ii = 0; ii < iterationCount; ii++)
            {
                //System.Threading.Thread.Sleep(1000);
                timer2.Restart();
                key = Guid.NewGuid().ToString();
                data = new byte[rnd.Next(500, 100000)];//~0.5- ~100Kb
                gen.GetBytes(data);
                _adapter.Add(key, data, DateTime.UtcNow.AddSeconds(1000), "");
                _adapter.Get(key);
                sbr.AppendFormat("{0},{1}\n", data.Length.ToString(), timer2.Elapsed.TotalMilliseconds.ToString("F2"));
            }
            timer.Stop();
            _adapter.RemoveAll();
            System.Diagnostics.Trace.WriteLine(string.Format("Speedtest for {0} completed in {1} ms. Avg: {2} ms.", iterationCount.ToString(), timer.ElapsedMilliseconds.ToString(), (Convert.ToDecimal(timer.ElapsedMilliseconds) / Convert.ToDecimal(iterationCount)).ToString("F2")));
            System.IO.File.WriteAllText(@"C:\outputTest014_SpeedtestSql.csv", sbr.ToString());
        }

        [TestMethod]
        public void Test015_ShouldGetLargeExistingObjectFromCache()
        {
            string key = "987625765";
            byte[] data = new byte[100000];//~100Kb;
            System.Security.Cryptography.RandomNumberGenerator gen = System.Security.Cryptography.RandomNumberGenerator.Create();
            gen.GetBytes(data);

            _adapter.Add(key, data, DateTime.UtcNow.AddSeconds(100), "");
            object obj = _adapter.Get(key);

            Assert.IsInstanceOfType(obj, typeof(byte[]));
            byte[] data2 = (obj as byte[]);

            Assert.AreEqual(data.Length, data2.Length);
            for (int ii = 0; ii < data.Length; ii++)
                Assert.AreEqual(data[ii], data2[ii]);

            _adapter.Remove(key);
        }

        [TestMethod]
        public void Test016_SetGetWithCompression()
        {
             _adapter.Dispose();
            _adapter.UseCompression = true; //UseCompression
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();
            string key = Guid.NewGuid().ToString();
            Random rnd = new Random(DateTime.Today.Millisecond);
            byte[] data = new byte[rnd.Next(200000, 1000000)];
            System.Security.Cryptography.RandomNumberGenerator gen = System.Security.Cryptography.RandomNumberGenerator.Create();
            gen.GetBytes(data);
            _adapter.Add(key, data, DateTime.UtcNow.AddSeconds(1000), "");
            _adapter.Get(key);
            timer.Stop();
            _adapter.RemoveAll();
            System.Diagnostics.Trace.WriteLine(string.Format("SetGetWithCompression for {0} bytes completed in {1} ms.)", data.Length.ToString(), timer.ElapsedMilliseconds.ToString()));
        }

        [TestMethod]
        public void Test017_ShouldClearItemsForSpecificSession()
        {
            _adapter.RemoveAll();
            _adapter.Add("089776446", "Document # 5", DateTime.UtcNow.AddSeconds(100), "SESSION123");
            _adapter.Add("089776447", "Document # 6", DateTime.UtcNow.AddSeconds(100), "SESSION555");
            _adapter.Add("089776448", "Document # 7", DateTime.UtcNow.AddSeconds(100), "SESSION123");

            _adapter.Clear("SESSION123");
            long obj = _adapter.GetCount();

            Assert.AreEqual(1, obj, "The number of items should be 1.");
        }

        [TestMethod]
        public void Test018_ShouldRenewItemsForSpecificSession()
        {
            _adapter.RemoveAll();
            _adapter.Add("089776446", "Document # 5", DateTime.UtcNow.AddSeconds(100), "SESSION123");
            _adapter.Add("089776447", "Document # 6", DateTime.UtcNow.AddSeconds(100), "SESSION500");
            _adapter.Add("089776448", "Document # 7", DateTime.UtcNow.AddSeconds(100), "SESSION123");

            _adapter.RemoveExpired();
            long obj = _adapter.GetCount();
            Assert.AreEqual(3, obj, "The number of items should be 3.");

            _adapter.Renew("SESSION123", DateTime.UtcNow.AddSeconds(-100));
            _adapter.RemoveExpired();
            obj = _adapter.GetCount();
            Assert.AreEqual(1, obj, "The number of items should be 1.");
        }

        [TestMethod]
        public void Test019_ShouldRetrieveCollectionForSpecificSession()
        {
            _adapter.RemoveAll();
            _adapter.Add("089776446", "Document # 5", DateTime.UtcNow.AddSeconds(100), "SESSION123");
            _adapter.Add("089776447", "Document # 6", DateTime.UtcNow.AddSeconds(100), "SESSION500");
            _adapter.Add("089776448", "Document # 7", DateTime.UtcNow.AddSeconds(100), "SESSION123");

            List<CacheItemInfo> items = _adapter.RetrieveCollection("SESSION123", true);

            Assert.AreEqual(2, items.Count, "The number of items should be 2.");
        }

        [TestMethod]
        [Ignore()]
        public void Test020_Speedtest2Random()
        {
            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();
            System.Diagnostics.Stopwatch timer2 = new System.Diagnostics.Stopwatch();
            string key = "";
            List<string> keysCollection = new List<string>();
            byte[] data = null;
            Random rnd = new Random(DateTime.Today.Millisecond);
            Random rndKeyGetter = new Random(DateTime.Today.Millisecond);
            Random rndOp = new Random(DateTime.Today.Millisecond);
            int iterationCount = 30000;
            System.Security.Cryptography.RandomNumberGenerator gen = System.Security.Cryptography.RandomNumberGenerator.Create();
            //save 1 item
            key = Guid.NewGuid().ToString();
            keysCollection.Add(key);
            data = new byte[rnd.Next(500, 100000)];//~0.5- ~100Kb
            gen.GetBytes(data);
            _adapter.Add(key, data, DateTime.UtcNow.AddSeconds(10000), "");
            System.Text.StringBuilder sbr = new StringBuilder("DB,Op,Length,Duration\n");
            //run test
            for (int ii = 0; ii < iterationCount; ii++)
            {
                switch (rndOp.Next(4))
                {
                    case 0://add
                        key = Guid.NewGuid().ToString();
                        keysCollection.Add(key);
                        data = new byte[rnd.Next(5000, 1000000)];//~5Kb- ~1Mb
                        gen.GetBytes(data);
                        timer2.Restart();
                        _adapter.Add(key, data, DateTime.UtcNow.AddSeconds(10000), "");
                        sbr.AppendFormat("S,1,{0},{1}\n", data.Length.ToString(), timer2.Elapsed.TotalMilliseconds.ToString("F2"));
                        break;
                    case 1://get
                        key = keysCollection[rndKeyGetter.Next(0, keysCollection.Count)];
                        timer2.Restart();
                        data = (byte[])_adapter.Get(key);
                        sbr.AppendFormat("S,2,{0},{1}\n", data.Length.ToString(), timer2.Elapsed.TotalMilliseconds.ToString("F2"));
                        break;
                    case 2://remove
                        if (keysCollection.Count > 50)
                        {
                            key = keysCollection[rndKeyGetter.Next(0, keysCollection.Count)];
                            keysCollection.Remove(key);
                            timer2.Restart();
                            _adapter.Remove(key);
                            sbr.AppendFormat("S,3,{0},{1}\n", data.Length.ToString(), timer2.Elapsed.TotalMilliseconds.ToString("F2"));
                        }
                        break;
                    case 3://set (update)
                    case 4:
                        key = keysCollection[rndKeyGetter.Next(0, keysCollection.Count)];
                        data = new byte[rnd.Next(5000, 1000000)];//~5Kb- ~1Mb
                        gen.GetBytes(data);
                        timer2.Restart();
                        _adapter.TrySet(key, data, DateTime.UtcNow.AddSeconds(10000), "");
                        sbr.AppendFormat("S,4,{0},{1}\n", data.Length.ToString(), timer2.Elapsed.TotalMilliseconds.ToString("F2"));
                        break;
                }
            }
            timer.Stop();
            _adapter.RemoveAll();
            System.Diagnostics.Trace.WriteLine(string.Format("Speedtest for {0} completed in {1} ms. Avg: {2} ms.", iterationCount.ToString(), timer.ElapsedMilliseconds.ToString(), (Convert.ToDecimal(timer.ElapsedMilliseconds) / Convert.ToDecimal(iterationCount)).ToString("F2")));
            System.IO.File.WriteAllText(@"C:\outputTestRandom_SpeedtestSql.csv", sbr.ToString());
        }

        [TestMethod]
        public void Test021_ShouldRemoveObjectByMetatags1FromCache()
        {
            string key = "08977-1";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            _adapter.Remove(new string[] { "test=yes", "userid=123" });

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test022_ShouldRemoveObjectByMetatags2FromCache()
        {
            string key = "08977-2";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            _adapter.Remove(new string[] { "userid=123" });

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test023_ShouldRemoveObjectByMetatags3FromCache()
        {
            string key = "08977-3";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            _adapter.Remove(new string[] { "test=yes" });

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestMethod]
        public void Test024_ShouldNotRemoveObjectByMetatags4FromCache()
        {
            string key = "08977-4";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            _adapter.Remove(new string[] { "test=no" });

            var obj = _adapter.Get(key);

            Assert.IsNotNull(obj);
        }

        [TestMethod]
        public void Test025_ShouldNotRemoveObjectByMetatags5FromCache()
        {
            string key = "08977-5";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to be deleted", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=_yes", "user_id=1_23" });
            _adapter.Remove(new string[] { "user_id=1_23" });

            var obj = _adapter.Get(key);

            Assert.IsNull(obj);
        }

        [TestCleanup]
        public void Cleanup()
        {
            _adapter.Dispose();
        }

        [TestMethod]
        public void Test026_ShouldContainCachedObject()
        {
            string key = "08977-26";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to test", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            bool contains = _adapter.Contains(key);

            Assert.IsTrue(contains);
        }

        [TestMethod]
        public void Test027_ShouldNotContainCachedObject()
        {
            string key = "08977-27";

            _adapter.RemoveAll();
            _adapter.Add(key, "Document to test", DateTime.UtcNow.AddSeconds(100), "", new string[] { "test=yes", "userid=123" });
            _adapter.Remove(new string[] { "userid=123" });
            bool contains = _adapter.Contains(key);

            Assert.IsFalse(contains);
        }
    }
}
